/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
/*
 * Created on Dec 19, 2004
 *
 */
package edu.mit.csail.relo.jdt.builder;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.eclipse.core.internal.jobs.JobStatus;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;

import edu.mit.csail.relo.intro.preferences.PreferenceConstants;
import edu.mit.csail.relo.jdt.RJCore;
import edu.mit.csail.relo.jdt.ReloJDTPlugin;
import edu.mit.csail.relo.jdt.builder.asm.AsmBuildVisitor;
import edu.mit.csail.relo.store.ReloRdfRepository;
import edu.mit.csail.relo.store.ReloStoreInitializer;
import edu.mit.csail.relo.store.StoreUtil;

/**
 * TODO: we support both AsmBuildVisitor and ReloJDTBuildVisitor (should we change our name)?
 * @author vineet
 *
 */
public class ReloJDTBuilder extends IncrementalProjectBuilder implements ReloStoreInitializer {
    static final Logger logger = ReloJDTPlugin.getLogger(ReloJDTBuilder.class);

	public static final String BUILDER_ID = RJCore.PLUGIN_ID + ".builder.ReloJDTBuilder";
	
	public static final boolean ASM_PARSER = true;
	public static final boolean JDT_PARSER = !ASM_PARSER;

	static final String JAVA_SUFFIX = ".java";
	

    private static final class ReloFullBuildJob extends Job {
        private final Object project;
        private final ReloBuildVisitor builderResVisitor;

        private ReloFullBuildJob(String name, Object buildTgt, ReloBuildVisitor builderResVisitor) {
            super(name);
            this.project = buildTgt;
            this.builderResVisitor = builderResVisitor;
            setUser(true);
        }

        @Override
        protected IStatus run(IProgressMonitor monitor) {
            buildResource(project, monitor, builderResVisitor);
            return Status.OK_STATUS;
        }

        @Override
        public boolean belongsTo(Object family) {
            if (family == ReloFullBuildFamily)
                return true;
            else
                return false;
        }
    }

    private static List<ReloRDFBuildProcessor> getBuildProcessors() {
        List<ReloRDFBuildProcessor> processors = new LinkedList<ReloRDFBuildProcessor> ();
        StoreUtil.loadEclipseClasses(processors, ReloRDFBuildProcessor.class, "edu.mit.csail.relo.jdt.reloBuildProcessor");
        return processors;
    }


    public static final String ReloBuildJobName = "Building For Relo: ";
    public static final String ReloCleanJobName = "Cleaning For Relo: ";

    /* (non-Javadoc)
     * @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
     */
    @Override
    protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
        //System.out.println("ReloJDTBuilder.build called");
        IProject project = getProject();
        IResourceDelta resDelta = getDelta(project);
        final ReloRdfRepository reloRepo = StoreUtil.getDefaultStoreRepository();
    	logger.info("Building: " + getProject().getName() + " " + (resDelta == null ? "Full Build" : "Incremental Build"));
        if (resDelta != null)
        {
        	if( PreferenceConstants.disableIncreamentBuild)
        		return null;
        	//logger.debug("------------Parsing via ASM----------------");
        	// XXX Fragmentize this decision
        	if (ASM_PARSER) build(resDelta, new AsmBuildVisitor(reloRepo, getBuildProcessors(), project), monitor);
            //logger.debug("------------Parsing via JDT----------------");
            if (JDT_PARSER) build(resDelta, new ReloJDTBuildVisitor(reloRepo, getBuildProcessors(), project), monitor);
			// logger.debug("------------Parsing via BCEL-----------------");
			// build(project, new ReloBCELBuildVisitor(), monitor);
        }
        else
        {
        	// logger.debug("------------Parsing via ASM----------------");
    		if (ASM_PARSER) build(project, new AsmBuildVisitor(reloRepo, getBuildProcessors(), project), monitor);
    		// logger.debug("------------Parsing via JDT----------------");
            if (JDT_PARSER) build(project, new ReloJDTBuildVisitor(reloRepo, getBuildProcessors(), project), monitor);
			// logger.debug("------------Parsing via JDT----------------");
			// build(project, new ReloBCELBuildVisitor(), monitor);
        }
        return null;
    }

    protected static void build(Object buildTgt, ReloBuildVisitor builderResVisitor, IProgressMonitor monitor) throws CoreException {
        if (!(buildTgt instanceof IResource) && !(buildTgt instanceof IResourceDelta))
            throw new IllegalArgumentException("Don't know how to build type:   " + buildTgt.getClass());


        
        int size = getBuildSize(buildTgt);
        if (size == 0)
        	return;
        builderResVisitor.setTaskSize( size );

        try {
            // build
            // full build - create a new job so that we can give a more accurate progress
            
            // below data is put in with ContainmentHeirarchyExtractor.init -> eclipseResourceToRDFResource
            // addProjectMetadata(reloRepo, project);

        	String jobName = ReloBuildJobName;
            if (buildTgt instanceof IProject) 
            	jobName += builderResVisitor.getProjectName();
        	Job job = new ReloFullBuildJob(jobName, buildTgt, builderResVisitor);
            if (buildTgt instanceof IProject) 
            	job.setRule( (IProject)buildTgt);
            else
            	job.setRule(((IResourceDelta)buildTgt).getResource());
            job.setUser(true);
            
            job.schedule();
            // since the builder represents the same job as the current one,
            // wake it up from sleeping state (we are effectively boosting
            // its priority over other sleeping Jobs)
            job.wakeUp(); 
//            buildResource(buildTgt, monitor, builderResVisitor);
        } finally {
        }
    }
    
    protected static void clean(final IProject project) throws CoreException {
        ReloRdfRepository reloRepo = StoreUtil.getDefaultStoreRepository();
        ReloJDTBuildVisitor builderResVisitor = new ReloJDTBuildVisitor(reloRepo, getBuildProcessors(), project);

        builderResVisitor.setTaskSize( getBuildSize(project) );
        builderResVisitor.removeAnnotations = true;
        builderResVisitor.addAnnotations = false;
        
        // create a job so that we can have a progress indicator
    	String jobName = ReloCleanJobName + builderResVisitor.getProjectName();
        Job job = new ReloFullBuildJob(jobName, project, builderResVisitor);
        job.setRule(project.getWorkspace().getRoot());
        job.schedule();
    }
    

    public static final String ReloFullBuildFamily = "ReloJDTFullBuildFamily";
    
    private static int getBuildSize(Object buildTgt) {
        if (!(buildTgt instanceof IResource) && !(buildTgt instanceof IResourceDelta))
            throw new IllegalArgumentException("Don't know how to build type: " + buildTgt.getClass());

        ReloJDTSizeVisitor sizeVisitor = new ReloJDTSizeVisitor();
        try {
            if (buildTgt instanceof IResource)
                ((IResource)buildTgt).accept(sizeVisitor);
            else if (buildTgt instanceof IResourceDelta)
                ((IResourceDelta)buildTgt).accept(sizeVisitor);

        } catch (CoreException e) {
            logger.error("Unexpected exception", e);
        } catch (Throwable e) {
            logger.error("Unexpected exception", e);
        }
        
        return sizeVisitor.size;
    }
    
    private static void buildResource(Object buildTgt, IProgressMonitor monitor, ReloBuildVisitor builderResVisitor) {
        if (!(buildTgt instanceof IResource) && !(buildTgt instanceof IResourceDelta))
            throw new IllegalArgumentException("Don't know how to build type: " + buildTgt.getClass());

        long buildStart = 0;
        try {
            monitor.beginTask("Building Java Files.", builderResVisitor.getTaskSize());
            builderResVisitor.setMonitor(monitor);

            buildStart = System.currentTimeMillis();

            builderResVisitor.initProcessors();

            if (buildTgt instanceof IResource)
                ((IResource)buildTgt).accept(builderResVisitor);
            else if (buildTgt instanceof IResourceDelta)
                ((IResourceDelta)buildTgt).accept(builderResVisitor);
            
            builderResVisitor.finishProcessing();
        } catch (CoreException e) {
            logger.error("Unexpected exception", e);
        } catch (Throwable e) {
            logger.error("Unexpected exception", e);
        } finally {
            long buildEnd = System.currentTimeMillis();
            logger.info("Build time: " + (buildEnd - buildStart)/1000 + "s [" + builderResVisitor.getProjectName() + "]");
        }
    }

    public void initRepository(ReloRdfRepository reloRepo) {
    	/*
        StatementIterator si = reloRepo.getStatements(null, reloRepo.rdfType, RJCore.projectType);
        while (si.hasNext()) {
            Statement s = si.next();
            IProject proj = ReloCore.resourceToEclipseProject(reloRepo, s.getSubject());
            
            // we basically remove and add all annotations
            ReloJDTBuildVisitor buildVisitor = new ReloJDTBuildVisitor(reloRepo, getBuildProcessors(), proj);
            buildVisitor.removeAnnotations = true;
            buildVisitor.addAnnotations = true;
            try {
                build(proj, buildVisitor, null);
            } catch (Throwable e) {
                logger.error("Unexpected exception while upgrading repository", e);
            }
        }
        */
    }
    

}
